package com.hunan.api.catv.service.biz.message;

import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hunan.api.catv.common.CacheName;
import com.hunan.api.catv.entity.ServiceResult;
import com.hunan.api.catv.entity.dto.JgMessageDto;
import com.hunan.api.catv.entity.po.AppPushMessageRecord;
import com.hunan.api.catv.entity.po.AppPushUrl;
import com.hunan.api.catv.entity.po.MemberOpenidInfo;
import com.hunan.api.catv.entity.vo.MemberVo;
import com.hunan.api.catv.service.ResultService;
import com.hunan.api.catv.service.biz.system.SystemDictBiz;
import com.hunan.api.catv.service.impl.member.MemberService;
import com.hunan.api.catv.utils.PageUtils;
import com.hunan.api.catv.utils.RedisUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jiang zhuan
 * @version 1.0
 * @description 描述
 * @date 2021/6/21 0021
 * @package com.hunan.api.catv.service.biz
 * @company 湖南有线
 */
@Service
@CacheConfig(cacheNames = CacheName.jgMessage)
public class JgPushBiz extends ResultService {

    private static final String MESSAGE_GIVE_BACK_CACHE = CacheName.jgMessage + "::messageGiveBack";
    private static final String MESSAGE_NOT_SAME_TIMES = CacheName.jgMessage + "::notSameTimes";
    private static final Integer MESSAGE_NOT_SAME_THRESHOLD = 3;
    private static final String UNREAD_HEAD_NUM = "0";
    private static final String READ_HEAD_NUM = "1";
    private static final String JG_MEMBER_TYPE = "200";
    private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm";
    private static final String TIME_PATTERN = "HH:mm";
    private static final Integer UN_DELETED = 0;

    @Autowired
    RedisUtils redisUtils;

    @Autowired
    MemberService memberService;

    @Autowired
    SystemDictBiz systemDictBiz;

    @Autowired
    JgPushBiz jgPushBiz;

    private final String OFFICIAL_MESSAGE_AVATAR = "official_message_avatar";

    public ServiceResult messageGiveBack(String deviceNo, String msgId) {
        log.info("极光推送消息回馈接口，参数：[{}]-[{}]-[{}]", deviceNo, msgId, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHssmm")));
        List<String> hgetList = (List<String>) redisUtils.hget(MESSAGE_GIVE_BACK_CACHE, deviceNo);
        if (ObjectUtils.isEmpty(hgetList)) {
            List<String> list = new ArrayList<>();
            list.add(UNREAD_HEAD_NUM + msgId);
            return buildOK(redisUtils.hset(MESSAGE_GIVE_BACK_CACHE, deviceNo, list));
        }
        List<String> msgIds = hgetList.parallelStream().map(s -> s.substring(1)).collect(Collectors.toList());
        if (!msgIds.contains(msgId)) {
            hgetList.add(UNREAD_HEAD_NUM + msgId);
        }
        return buildOK(redisUtils.hset(MESSAGE_GIVE_BACK_CACHE, deviceNo, hgetList));
    }

    public ServiceResult readMessage(String msgId, Integer memberId) {
        log.info("极光推送读取消息接口，参数：[{}]-[{}]-[{}]", memberId, msgId, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHssmm")));
        List<MemberOpenidInfo> memberOpenidInfos = new MemberOpenidInfo().selectList(new QueryWrapper<MemberOpenidInfo>().eq(MemberOpenidInfo.MEMBER_ID, memberId).eq(MemberOpenidInfo.MEMBER_THIRD_TYPE, JG_MEMBER_TYPE).eq(MemberOpenidInfo.STATUS, 1).eq(MemberOpenidInfo.DELETED, 1));
        memberOpenidInfos.parallelStream().forEach(memberOpenidInfo -> {
            String memberThirdOpenId = memberOpenidInfo.getMemberThirdOpenId();
            List<String> hgetList = (List<String>) redisUtils.hget(MESSAGE_GIVE_BACK_CACHE, memberThirdOpenId);
            if (!ObjectUtils.isEmpty(hgetList)) {
                hgetList = hgetList.parallelStream().filter(s -> {
                            s = s.substring(1);
                            return !s.equals(msgId);
                        }
                ).collect(Collectors.toList());
                hgetList.add(READ_HEAD_NUM + msgId);
                redisUtils.hset(MESSAGE_GIVE_BACK_CACHE, memberThirdOpenId, hgetList);
            }
        });
        return buildOK(true);
    }

    public PageInfo<JgMessageDto> page(Integer pageNum, Integer pageSize, Integer memberId, String deviceNo) {
        log.info("极光推送消息列表接口，参数：[{}]-[{}]-[{}]-[{}]", pageNum, pageSize, deviceNo, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHssmm")));
        List<String> deviceNos = new ArrayList<>();
        if (memberId != null) {
            List<MemberOpenidInfo> memberOpenidInfos = new MemberOpenidInfo().selectList(new QueryWrapper<MemberOpenidInfo>().eq(MemberOpenidInfo.MEMBER_ID, memberId).eq(MemberOpenidInfo.MEMBER_THIRD_TYPE, JG_MEMBER_TYPE).eq(MemberOpenidInfo.STATUS, 1).eq(MemberOpenidInfo.DELETED, 1));
            List<String> collect = new ArrayList<>();
            memberOpenidInfos.parallelStream().forEach(memberOpenidInfo -> {
                String memberThirdOpenId = memberOpenidInfo.getMemberThirdOpenId();
                deviceNos.add(memberThirdOpenId);
                List<String> hgetList = (List<String>) redisUtils.hget(MESSAGE_GIVE_BACK_CACHE, memberThirdOpenId);
                if (!ObjectUtils.isEmpty(hgetList)) {
                    collect.addAll(hgetList);
                }
            });
            return wrapJgMessageDtoPageInfo(pageNum, pageSize, collect, deviceNos);
        } else {
            List<String> hgetList = (List<String>) redisUtils.hget(MESSAGE_GIVE_BACK_CACHE, deviceNo);
            deviceNos.add(deviceNo);
            return wrapJgMessageDtoPageInfo(pageNum, pageSize, hgetList, deviceNos);
        }
    }

    private PageInfo<JgMessageDto> wrapJgMessageDtoPageInfo(Integer pageNum, Integer pageSize, List<String> collect, List<String> deviceNos) {
        PageInfo<JgMessageDto> jgMessageDtoPageInfo = jgPushBiz.getJgMessageDtoPageInfo(pageNum, pageSize, collect, deviceNos);
        List<JgMessageDto> list = jgMessageDtoPageInfo.getList();
        if (ObjectUtils.isEmpty(list)) {
            return jgMessageDtoPageInfo;
        }
        list = list.parallelStream().map(jgMessageDto -> getWrapTime(jgMessageDto)
        ).collect(Collectors.toList());
        list = list.stream().sorted(Comparator.comparing(JgMessageDto::getSeqTime).reversed()).collect(Collectors.toList());
        jgMessageDtoPageInfo.setList(list);
        return jgMessageDtoPageInfo;
    }

    private JgMessageDto getWrapTime(JgMessageDto jgMessageDto) {
        String createTime = jgMessageDto.getMessageDateTime();
        if (StringUtils.isNotBlank(createTime)) {
            LocalDateTime ctime = LocalDateTime.parse(createTime, DateTimeFormatter.ofPattern(DATE_TIME_PATTERN));
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime startTimeTheDay = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
            LocalDateTime endTimeTheDay = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
//            Duration between = Duration.between(LocalDateTime.now(), endTime);
            if (ctime.isBefore(endTimeTheDay) && ctime.isAfter(startTimeTheDay)) {
                jgMessageDto.setMessageDateTime(ctime.format(DateTimeFormatter.ofPattern(TIME_PATTERN)));
                return jgMessageDto;
            }
            if (ctime.isBefore(startTimeTheDay) && ctime.plusDays(7L).compareTo(now) > 0) {
                jgMessageDto.setMessageDateTime(getWeekName(ctime.getDayOfWeek().getValue()) + " " + ctime.format(DateTimeFormatter.ofPattern(TIME_PATTERN)));
                return jgMessageDto;
            }
        }
        return jgMessageDto;
    }

    private String getWeekName(int value) {
        String name = "";
        switch (value) {
            case 1:
                name = "周一";
                break;
            case 2:
                name = "周二";
                break;
            case 3:
                name = "周三";
                break;
            case 4:
                name = "周四";
                break;
            case 5:
                name = "周五";
                break;
            case 6:
                name = "周六";
                break;
            case 7:
                name = "周日";
                break;
            default:
                break;
        }
        return name;
    }

    private PageInfo<JgMessageDto> getJgMessageDtoPageInfo(Integer pageNum, Integer pageSize, List<String> collect, List<String> deviceNos) {
        Map<String, Integer> msgIsReadMap = null;
        Set<String> msgIds = null;
        if (collect != null && !collect.isEmpty()) {
            msgIsReadMap = collect.parallelStream().collect(Collectors.toMap(s -> s.substring(1), s -> {
                String head = s.substring(0, 1);
                return head.equals(UNREAD_HEAD_NUM) ? 0 : 1;
            }, (v1, v2) -> v1));
            msgIds = collect.parallelStream().map(s -> s.substring(1)).collect(Collectors.toSet());
        }
        if (msgIds != null && !msgIds.isEmpty()) {
            PageHelper.startPage(pageNum, pageSize);
            List<AppPushMessageRecord> appPushMessageRecords = new AppPushMessageRecord().selectList(new QueryWrapper<AppPushMessageRecord>().eq(AppPushMessageRecord.TYPE, 1).eq(AppPushMessageRecord.DELETED, UN_DELETED).in(AppPushMessageRecord.EXTEND4, msgIds).orderByDesc(AppPushMessageRecord.PUSH_TIME));
            PageInfo<AppPushMessageRecord> appPushMessageRecordPageInfo = new PageInfo<>(appPushMessageRecords);
            List<AppPushMessageRecord> list = appPushMessageRecordPageInfo.getList();
            long total = appPushMessageRecordPageInfo.getTotal();
            List<Integer> pushIds = list.parallelStream().map(AppPushMessageRecord::getId).collect(Collectors.toList());
            Map<String, AppPushUrl> idUrlMap = null;
            if (!ObjectUtils.isEmpty(pushIds)) {
                List<AppPushUrl> appPushUrls = new AppPushUrl().selectList(new QueryWrapper<AppPushUrl>().in(AppPushUrl.PUSH_ID, pushIds).eq(AppPushUrl.DELETED, UN_DELETED));
                if (!ObjectUtils.isEmpty(appPushUrls)) {
                    idUrlMap = appPushUrls.parallelStream().collect(Collectors.toMap(appPushUrl -> String.valueOf(appPushUrl.getPushId()), appPushUrl -> appPushUrl, (v1, v2) -> v1));
                }
            }
            Map<String, AppPushUrl> finalIdUrlMap = idUrlMap;
            Map<String, Integer> finalMsgIsReadMap = msgIsReadMap;
            List<JgMessageDto> jgMessageDtos = list.parallelStream().map(appPushMessageRecord -> {
                JgMessageDto jgMessageDto = new JgMessageDto();
                jgMessageDto.setTitle(appPushMessageRecord.getPushTitle());
                jgMessageDto.setContent(appPushMessageRecord.getPushContent());
                jgMessageDto.setNickName(appPushMessageRecord.getSenderName());
                Integer senderId = appPushMessageRecord.getSenderId();
                if (senderId == null || senderId.equals(0)) {
                    String officialAvatar = systemDictBiz.getStringByKey(OFFICIAL_MESSAGE_AVATAR);
                    jgMessageDto.setAvatarUrl(officialAvatar);
                } else {
                    MemberVo memberVo = Optional.ofNullable(memberService.getById(senderId)).orElse(new MemberVo());
                    jgMessageDto.setAvatarUrl(memberVo.getAvatarUrl());
//                    jgMessageDto.setNickName(memberVo.getNickName());
                }
                AppPushUrl appPushUrl = Optional.ofNullable(finalIdUrlMap.get(String.valueOf(appPushMessageRecord.getId()))).orElse(new AppPushUrl());
                jgMessageDto.setType(appPushUrl.getPushAims());
                jgMessageDto.setLinks(appPushUrl.getLinks());
                jgMessageDto.setMsgId(appPushMessageRecord.getExtend4());
                jgMessageDto.setCoverImage(appPushMessageRecord.getPushImage());
                Date pushTime = appPushMessageRecord.getPushTime();
                if (pushTime != null) {
                    String messageDateTime = pushTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().format(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN));
                    jgMessageDto.setMessageDateTime(messageDateTime);
                    jgMessageDto.setSeqTime(pushTime);
                }
                jgMessageDto.setState(finalMsgIsReadMap.get(appPushMessageRecord.getExtend4()));
                return jgMessageDto;
            }).collect(Collectors.toList());
            keepStatisticsNumberidentical(msgIds, deviceNos,total);
            return PageUtils.list2PageInfoForSearch(pageNum, pageSize, jgMessageDtos, (int) total);
        } else {
            return new PageInfo<>();
        }
    }

    private void keepStatisticsNumberidentical(Set<String> msgIds, List<String> deviceNos, long total) {
        if (ObjectUtils.isEmpty(msgIds)) {
            return;
        }
        if (total < 1) {
            for (String deviceNo : deviceNos) {
                redisUtils.hdel(MESSAGE_GIVE_BACK_CACHE, deviceNo);
            }
            return;
        }
        if (msgIds.size() > total) {
            List<AppPushMessageRecord> appPushMessageRecords = new AppPushMessageRecord().selectList(new QueryWrapper<AppPushMessageRecord>().eq(AppPushMessageRecord.TYPE, 1).eq(AppPushMessageRecord.DELETED, UN_DELETED).in(AppPushMessageRecord.EXTEND4, msgIds));
            List<String> exsit = appPushMessageRecords.parallelStream().map(AppPushMessageRecord::getExtend4).collect(Collectors.toList());
            msgIds.removeAll(exsit);
            for (String deviceNo : deviceNos) {
                Integer hget = (Integer) Optional.ofNullable(redisUtils.hget(MESSAGE_NOT_SAME_TIMES, deviceNo)).orElse(0);
                if (hget < MESSAGE_NOT_SAME_THRESHOLD) {
                    redisUtils.hset(MESSAGE_NOT_SAME_TIMES, deviceNo, hget + 1);
                    continue;
                } else {
                    redisUtils.hset(MESSAGE_NOT_SAME_TIMES, deviceNo, 0);
                }
                List<String> hgetList = (List<String>) redisUtils.hget(MESSAGE_GIVE_BACK_CACHE, deviceNo);
                if (!ObjectUtils.isEmpty(hgetList)) {
                    hgetList = hgetList.parallelStream().filter(s -> {
                                s = s.substring(1);
                                return !msgIds.contains(s);
                            }
                    ).collect(Collectors.toList());
                    redisUtils.hset(MESSAGE_GIVE_BACK_CACHE, deviceNo, hgetList);
                }
            }
        }

    }


    public JSONObject readStatistics(String deviceNo, Integer memberId) {
        log.info("极光推送统计未读消息接口，参数：[{}]-[{}]-[{}]", memberId, deviceNo, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHssmm")));
        List<String> collect = new ArrayList<>();
        if (memberId != null) {
            List<MemberOpenidInfo> memberOpenidInfos = new MemberOpenidInfo().selectList(new QueryWrapper<MemberOpenidInfo>().eq(MemberOpenidInfo.MEMBER_ID, memberId).eq(MemberOpenidInfo.MEMBER_THIRD_TYPE, JG_MEMBER_TYPE).eq(MemberOpenidInfo.STATUS, 1).eq(MemberOpenidInfo.DELETED, 1));
            memberOpenidInfos.parallelStream().forEach(memberOpenidInfo -> {
                String memberThirdOpenId = memberOpenidInfo.getMemberThirdOpenId();
                List<String> hgetList = (List<String>) redisUtils.hget(MESSAGE_GIVE_BACK_CACHE, memberThirdOpenId);
                if (!ObjectUtils.isEmpty(hgetList)) {
                    collect.addAll(hgetList);
                }
            });
            return statisticsReadResult(collect);
        } else {
            List<String> hgetList = (List<String>) redisUtils.hget(MESSAGE_GIVE_BACK_CACHE, deviceNo);
            return statisticsReadResult(hgetList);
        }
    }

    private JSONObject statisticsReadResult(List<String> msgIds) {
        JSONObject result = new JSONObject();
        result.set("existUnRead", false);
        long count = 0;
        if (msgIds != null && !msgIds.isEmpty()) {
            count = msgIds.parallelStream().distinct().filter(s -> s.startsWith(UNREAD_HEAD_NUM)).count();
        }
        if (count > 0) {
            result.set("existUnRead", true);
            result.set("number", count);
        }
        return result;
    }

}
